//------------------------------------------------------------------------------
//	file sm.cpp
//	brief
//		State machine implementation file
//
//	$Id: sm.cpp 230 2014-01-05 10:47:22Z ac.verbeck@gmail.com $
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//	Copyright and Disclaimer Notice
//
//	Copyright (c) 2012 A.C. Verbeck
//
//	This file is part of SM: A State Machine Library.
//
//	SM is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	SM is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with SM.  If not, see <http://www.gnu.org/licenses/>.
//
//------------------------------------------------------------------------------
#include <stdint.h>																//	Standard int types
#include <stdbool.h>															//	Bool
#include <stdlib.h>																//	NULL

#include "sm.h"

//------------------------------------------------------------------------------
//	brief
//		state machine constructor
//
//	param
//		STATE_FN		st:  State table
//	param
//		uint32_t		ms:  Max state
//	param
//		STATE_INIT*		si:  State initialization
//
// return
//		none
//------------------------------------------------------------------------------
SM::SM(const SM_STATE_FN st[], uint32_t ms, STATE_INIT* si)
{
	c_state = si->c_state;														//	current state
	n_state = si->n_state;														//	next state
	p_state = si->p_state;														//	previous state
	m_state = ms;																//	max size of state array: so we don't go off the end
	status = SM_OK;																//	status
	change = false;																//	change state

	st_arr = new SM_STATE_FN[ms];												//	allocate state array
	for (uint32_t i=0; i<ms; i++)												//	loop through the state array
		st_arr[i] = st[i];														//	initialize from incoming data

	if (c_state < m_state)														//	If current state is less than max state...
		status = (*st_arr[c_state].enter)();									//  ...enter state
	else																		//	If not...
		status = SM_SC_ERROR;						 							//  ...set state count error

}
SM::~SM(void)
{
	delete[] st_arr;															//	Delete the dynamically allocated array
}

//------------------------------------------------------------------------------
//	brief
//		Execute current state
//
//	param
//		STATE_MACHINE*  sm:	Pointer to current state machine
//	param
//		STATE_FN		st:  State table
//	param
//		EVENT		ev: Event to be processed
//
// return
//		State machine status
//------------------------------------------------------------------------------
SM_STATUS SM::Process(SM_EVENT ev)
{
	status = (*st_arr[c_state].process)(ev);									//  process current state

	if (change == true) {														//	If SM to change states...
		(*st_arr[c_state].exit)();												//  ...exit old state...
		p_state = c_state;			   											//  ...set prev state...
		c_state = n_state;			   											//  ...set new state...
		(*st_arr[c_state].enter)();												//  ...enter new state...
		change = false;															//	...reset state change
	}

	return status;
}

//------------------------------------------------------------------------------
//	brief
//		State Machine state change
//		These functions set a new state into the state machine
//		- StateChange: Change to a new state
//		- StatePrev:   Change to the previous state
//
//	param
//		STATE 		st: Next state
//
// return
//		State machine status
//------------------------------------------------------------------------------
SM_STATUS SM::StateChange(SM_STATE st)
{
	if (st == c_state) return status;											//	If already in state, exit

	if (st < m_state) {															//	If less than max state...
		n_state = st;															//	...set new state...
		change = true;															//	...set state change to true...
		status = SM_OK;						 									//  ...set status
	} else																		//	If new state is not in range...
		status = SM_SC_ERROR;						 							//  ...set state count error

	return status;																//	Return state change status
}
SM_STATUS SM::StatePrev(void)
{
	n_state = p_state;															//	Set state to previous state
	change = true;																//	Set state change to true
	status = SM_OK;						 										//  Set state machine status to OK

	return status;																//	Return state change status
}

//------------------------------------------------------------------------------
//	brief
//		Access function to retrieve state machine current state
//
// return
//		Current state
//------------------------------------------------------------------------------
SM_STATE SM::GetState(void)
{
	return c_state;
}

//------------------------------------------------------------------------------
//	brief
//		Access function to retrieve state machine current status
//
// return
//		State machine status
//------------------------------------------------------------------------------
SM_STATUS SM::GetStatus(void)
{
	return status;
}

//
//	End: sm.cpp
//
